package com.lanyam.travel.base.controller;

import java.security.AlgorithmParameters;
import java.security.Security;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

import javax.annotation.Resource;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;

import org.apache.log4j.Logger;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.util.Base64Utils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.aliyuncs.exceptions.ClientException;
import com.lanyam.travel.base.entity.Role;
import com.lanyam.travel.base.entity.User;
import com.lanyam.travel.base.entity.base.OpenidToken;
import com.lanyam.travel.base.entity.base.PhoneToken;
import com.lanyam.travel.base.service.UserService;
import com.lanyam.travel.base.utils.AliyunSmsUtils;
import com.lanyam.travel.base.utils.Constant;
import com.lanyam.travel.base.utils.JSONResult;
import com.lanyam.travel.base.utils.JwtUtils;
import com.lanyam.travel.base.utils.MD5Utils;
import com.lanyam.travel.base.utils.RedisUtil;
import com.lanyam.travel.base.utils.SendSMSUtils;
import com.lanyam.travel.base.utils.WXCodeUtil;


@RestController
@RequestMapping("login")
public class RegistLoginController extends BasicController{
	@Autowired
	private UserService userService;
	

	
	@Resource
    private RedisUtil redisUtil;
	
	Logger logger = Logger.getLogger(RegistLoginController.class);
	
	/**
	 * 用户注册
	 * @return
	 * @throws Exception
	 */
	@RequestMapping(value = "/regist",method = RequestMethod.POST)
	public JSONResult regist(String openid,String nickname,String avatarUrl) throws Exception {
//	public JSONResult regist(@RequestBody Map<String,String> reqPara) throws Exception {
//		String phone = reqPara.get("phone");
//		String password = reqPara.get("password");
		// 1. 判断用户名和密码必须不为空
		if (StringUtils.isEmpty(openid) || StringUtils.isEmpty(openid)) {
			return JSONResult.errorMsg("openid为空");
		}
		
		// 2. 判断手机号是否存在
		User existUser = userService.getUserByOpenid(openid);
		// 3. 保存用户，注册信息
		if (null == existUser) {
			User user = new User();
			//String id = UUID.randomUUID().toString().replace("-", "");
			user.setId(openid);
			user.setNickName(nickname);
			user.setName(nickname);
			user.setFaceImg(avatarUrl);
			user.setPhone("");
			user.setPassword("");
			user.setStatus(1);
			user.setOpenid(openid);
			user.setCreateDate(new Date());
			user.setUpdateDate(new Date());
			userService.saveUser(user);
			user.setPassword("");
			return JSONResult.ok(user);
		} else {
			return JSONResult.errorMsg("该手机号已注册，请更换手机号");
		}
	}
	/**
	 * 后台管理登陆，只允许管理员登陆
	 * @param phone
	 * @param password
	 * @return
	 */
	@PostMapping("/adminLogin")
	public JSONResult adminLogin(String phone,String password)
	{
		if(StringUtils.isEmpty(phone)||StringUtils.isEmpty(password))
		{
			return JSONResult.errorMsg("手机号密码不能为空");
		}
		Subject subject = SecurityUtils.getSubject();
		String id,token;
		
		User user = null;
		try {
			user = userService.queryUserForLogin(phone, MD5Utils.getMD5Str(password));
			// 3. 返回
			if (null == user)
			{
				return JSONResult.errorMsg("用户名或密码不正确, 请重试...");
			}
			Role role = userService.getRoleByUserId(user.getId());
			if(null == role || role.getPower() == 0)
			{
				return JSONResult.errorMsg("您不具有管理员权限，不能登陆。");
			}
			UsernamePasswordToken nameToken = new UsernamePasswordToken(phone,MD5Utils.getMD5Str(password));
            //登陆验证。验证成功后，会将token存入session中
            subject.login(nameToken);
            token = subject.getSession().getId().toString();
            user.setToken(token);
			redisUtil.set(token, user, 2*60*60);
			user.setPassword("");
			return JSONResult.ok(user);
		} catch (UnknownAccountException e) {
			// TODO: handle exception
			logger.error("登陆失败："+e.getMessage());
			return JSONResult.errorMsg("账号不存在");
		} catch(IncorrectCredentialsException e){
			logger.error("登陆失败："+e.getMessage());
			return JSONResult.errorMsg("账号或密码不正确");
		} catch(LockedAccountException e){
			logger.error("登陆失败："+e.getMessage());
			return JSONResult.errorMsg("账号已被锁定,请联系管理员");
		} catch(Exception e){
			logger.error("登陆失败："+e.getMessage());
			return JSONResult.errorMsg("登陆失败");
		} 
			
	}
	/**
	 * 用户登陆
	 * @param phone
	 * @param password 不需要的话传空值
	 * @param loginType 登陆类型：0为微信号登陆，1为手机号密码登陆，2为手机号验证码登陆
	 * @return
	 * @throws Exception
	 */
	@PostMapping(value = "/login")
	@ResponseBody
	public JSONResult login(@RequestParam("phone") String phone,
			@RequestParam(value = "password",required = false) String password,
			@RequestParam("loginType") int loginType) throws Exception {
		if(StringUtils.isEmpty(phone))
		{
			return JSONResult.errorMsg("请输入手机号！");
		}
		try {
			Subject subject = SecurityUtils.getSubject();
			String id,token;
			int role;
			User user = null;
			if(loginType == 0)
			{
				//微信登陆，如果手机号已存在，不需要保存到数据库，如果不存在，保存到数据库；并设置token返回
				user = userService.queryPhoneIsExist(phone);
				if(null == user)
				{
					user = new User();
					id = UUID.randomUUID().toString().replace("-", "");
					user.setId(id);
					user.setPhone(phone);
					user.setStatus(1);
					user.setCreateDate(new Date());
					user.setUpdateDate(new Date());
					userService.saveUser(user);
				}
		        PhoneToken phoneToken = new PhoneToken(phone);
	            //登陆验证。验证成功后，会将token存入session中
	            subject.login(phoneToken);
	            token = subject.getSession().getId().toString();
			}
			else if(loginType == 1)
			{
				if(StringUtils.isEmpty(phone) || StringUtils.isEmpty(password))
				{
					// 1. 判断用户名和密码必须不为空
					return JSONResult.errorMsg("用户名或密码不能为空...");
				}
				user = userService.queryUserForLogin(phone, 
						MD5Utils.getMD5Str(password));
				// 3. 返回
				if (null == user)
				{
					return JSONResult.errorMsg("用户名或密码不正确, 请重试...");
				}
//				role = userResult.getRole();
				UsernamePasswordToken nameToken = new UsernamePasswordToken(phone,MD5Utils.getMD5Str(password));
	            //登陆验证。验证成功后，会将token存入session中
	            subject.login(nameToken);
	            token = subject.getSession().getId().toString();
			}
			else if(loginType == 2)
			{
				String sessionValidateCode = (String) redisUtil.get(Constant.PHONE_VALIDATE_CODE+":"+phone);
				if(StringUtils.isEmpty(sessionValidateCode) || StringUtils.isEmpty(password))
				{
					return JSONResult.errorMsg("未获取到验证码，请稍后重试！");
				}
				if(sessionValidateCode.equals(password))
				{
					user = userService.queryPhoneIsExist(phone);
					if(null == user)
					{
						user = new User();
						id = UUID.randomUUID().toString().replace("-", "");
						user.setId(id);
						user.setPhone(phone);
						user.setStatus(1);
						user.setCreateDate(new Date());
						user.setUpdateDate(new Date());
						userService.saveUser(user);
					}
					PhoneToken phoneToken = new PhoneToken(phone);
		            //登陆验证。验证成功后，会将token存入session中
		            subject.login(phoneToken);
		            token = subject.getSession().getId().toString();
				}
				else
				{
					return JSONResult.errorMsg("验证码错误！");
				}
			}
			else
			{
				return JSONResult.errorMsg("未获取到登陆信息");
			}
			user.setToken(token);
			redisUtil.set(token, user, 2*60*60);
			user.setPassword("");
			return JSONResult.ok(user);
			
		} catch (UnknownAccountException e) {
			// TODO: handle exception
			logger.error("登陆失败："+e.getMessage());
			return JSONResult.errorMsg("账号不存在");
		} catch(IncorrectCredentialsException e){
			logger.error("登陆失败："+e.getMessage());
			return JSONResult.errorMsg("账号或密码不正确");
		} catch(LockedAccountException e){
			logger.error("登陆失败："+e.getMessage());
			return JSONResult.errorMsg("账号已被锁定,请联系管理员");
		} catch(Exception e){
			logger.error("登陆失败："+e.getMessage());
			return JSONResult.errorMsg("登陆失败");
		} 
		
	}
	/**
	 * 	退出登陆
	 * @param userId 用户Id
	 * @return
	 * @throws Exception
	 */
	@PostMapping("/logout")
	@ResponseBody
	public JSONResult logout(String userId) throws Exception {
//		redisUtil.del(Constant.USER_REDIS_SESSION + ":" + userId);
		Subject subject = SecurityUtils.getSubject();
		subject.logout();
		return JSONResult.ok();
	}
	/**
	 *  	获取微信用户绑定的手机号
	 * @param encryptedData
	 * @param session_key
	 * @param iv
	 * @return
	 */
	@PostMapping("/getWXxPhone")
	@ResponseBody
	public JSONResult getWXxPhone(String encryptedData, String session_key, String iv)
	{
		// 被加密的数据
		byte[] dataByte = Base64Utils.decodeFromString(encryptedData);
		byte[] keyByte = Base64Utils.decodeFromString(session_key);
		byte[] ivByte = Base64Utils.decodeFromString(iv);
        try {
           // 如果密钥不足16位，那么就补足.  这个if 中的内容很重要
           int base = 16;
           if (keyByte.length % base != 0) {
               int groups = keyByte.length / base + (keyByte.length % base != 0 ? 1 : 0);
               byte[] temp = new byte[groups * base];
               Arrays.fill(temp, (byte) 0);
               System.arraycopy(keyByte, 0, temp, 0, keyByte.length);
               keyByte = temp;
           }
           // 初始化
           // 需要引pom          
           Security.addProvider(new BouncyCastleProvider());
           Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
           SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");
           AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");
           parameters.init(new IvParameterSpec(ivByte));
           cipher.init(Cipher.DECRYPT_MODE, spec, parameters);// 初始化
           byte[] resultByte = cipher.doFinal(dataByte);
           if (null != resultByte && resultByte.length > 0) {
               String result = new String(resultByte, "UTF-8");
               return JSONResult.ok(JSONObject.parseObject(result));
           }
       } catch (Exception e) {
           e.printStackTrace();
       }
       return null;
	}
	/**
	 * 	获取手机验证码
	 * @param phone
	 * @return
	 */
	@PostMapping("/getValidateCode")
	@ResponseBody
	public JSONResult getValidateCode(String phone)
	{
		//生成6位数随机验证码
		StringBuffer buf = new StringBuffer();
		for (int i = 0; i < 6; i++) {
			buf.append((int)(Math.random()*9+1));
		}
		String validateCode = buf.toString();
		Map<String,String> paraMap = new HashMap<String, String>();
		paraMap.put("randomCode", validateCode);
		String result = SendSMSUtils.doSend(phone, paraMap, Constant.MESSAGE_TEMPLATE_NAME_BAND);
		if("OK".equals(result))
		{
			redisUtil.set(Constant.PHONE_VALIDATE_CODE +":"+phone, validateCode,60 * 5);
			return JSONResult.ok();
		}
		else if(Constant.MESSAGE_TEMPLATE_NAME_ERROR.equals(result))
		{
			logger.error("短信发送模板不存在。");
			return JSONResult.errorMsg("服务器故障，请稍后重试");
		}
		else
		{
			logger.error("短信发送失败，原因为："+result);
			return JSONResult.errorMsg("短信发送失败，原因为："+result);
		}
		
	}
	
	/**
	 * 获取微信授权用户登陆信息,能获取到session_key和openid,如果能获取到信息，则登陆成功
	 * @param code 前台调用wx.login获取额值
	 * @return session_key和openid
	 * @throws Exception
	 */
	@GetMapping("/wxLogin")
	@ResponseBody
	public JSONResult wxLogin(String code) throws Exception
	{
		String appid = Constant.APP_ID;
		String secret = Constant.APP_SECRET;
		String info = WXCodeUtil.getWxLoginInfo(appid, secret, code);
		//能获取到微信用户登陆信息，则可以登陆，如果是第一次登陆，需要保存用户信息
		if(!StringUtils.isEmpty(info))
		{
			JSONObject jsonObject = JSON.parseObject(info);
			String openid = (String) jsonObject.get("openid");
			User user = userService.getUserByOpenid(openid);
			if(null == user)
			{
				user = new User();
				String id = UUID.randomUUID().toString().replace("-", "");
				user.setId(id);
				user.setOpenid(openid);
				user.setStatus(1);
				user.setCreateDate(new Date());
				user.setUpdateDate(new Date());
				userService.saveUser(user);
			}
			Subject subject = SecurityUtils.getSubject();
	        OpenidToken openidToken = new OpenidToken(openid);
            //登陆验证。验证成功后，会将token存入session中
            subject.login(openidToken);
            String token = subject.getSession().getId().toString();
            System.out.println(token);
            jsonObject.put("token", token);
			return JSONResult.ok(jsonObject);
		}
		return JSONResult.errorMsg("微信登陆失败，未获取到用户信息。");
	}
	/**
	 * 	通过Shiro验证登陆
	 * https://www.jianshu.com/p/0366a1675bb6?utm_campaign
	 * @param user
	 * @return
	 */
	@RequestMapping("/login2")
    public String login2(User user) {
        //添加用户认证信息
		System.out.println("login2");
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(
            user.getPhone(),
            user.getPassword()
        );
        try {
            //进行验证，这里可以捕获异常，然后返回对应信息
            subject.login(usernamePasswordToken);
            subject.checkRole("admin");
            subject.checkPermissions("query", "add");
        } catch (AuthenticationException e) {
            e.printStackTrace();
            return "账号或密码错误！";
        } catch (AuthorizationException e) {
            e.printStackTrace();
            return "没有权限";
        }
        return "login success";
    }
	
	
	
     //自动验证已登陆用户的角色和权限
    @RequiresRoles("admin")
    @RequiresPermissions("create")
    @GetMapping("/index")
    public String index() {
        return "index!";
    }
    //不需要的登陆验证
    @GetMapping("/hello")
    public String hello()
    {
    	return "hello!";
    }

	@GetMapping("/unauthorized")
	public String unauthorized()
	{
		return "未通过认证，请先登陆.";
	}
	
    
    @RequestMapping("/login3")
    public String login3(String phone,String password) throws Exception {
        //添加用户认证信息
        Subject subject = SecurityUtils.getSubject();
        String md5Password = MD5Utils.getMD5Str(password);
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(phone,md5Password);
        try {
            //登陆验证。验证成功后，会将token存入session中
            subject.login(usernamePasswordToken);
            Session session = subject.getSession();
            String token = session.getId().toString();
            System.out.println(token);
            return token;
//            subject.checkRole("admin");
//            subject.checkPermissions("query", "add");
        } catch (AuthenticationException e) {
            e.printStackTrace();
            return "账号或密码错误！";
        } catch (AuthorizationException e) {
            e.printStackTrace();
            return "没有权限";
        }
//        return "login success";
    }
    
}
